//
// ReflectionFu.cs
//
// Copyright (C) 2004-2006 Novell, Inc.
//

//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//

using System;
using System.IO;
using System.Reflection;
using System.Collections.Generic;

namespace Dashboard.Util 
{
	public class ReflectionFu 
	{
		public delegate void ForeachDirMethod (Assembly a);
		
		static public List<Assembly> ScanEnvironmentForAssemblies (string env_var, string fallback_path)
		{
			return ScanEnvironmentForAssemblies (env_var, fallback_path, null);
		}

		static public List<Assembly> ScanEnvironmentForAssemblies (string env_var, string fallback_path, ForeachDirMethod MethodCallback)
		{
			List<Assembly> assemblies = new List<Assembly> ();

			string path = Environment.GetEnvironmentVariable (env_var);

			if (path == null || path == "")
				path = fallback_path;
			else if (path [path.Length-1] == ':')
				path += fallback_path;

			Dictionary<string, bool> seen = new Dictionary<string, bool> ();

			foreach (string dir in path.Split (':')) {
				if (! seen.ContainsKey (dir)) {
					List<Assembly> list = ScanDirectoryForAssemblies (dir);
					
					if (list == null)
						continue;

					foreach (Assembly a in list) {
						assemblies.Add (a);

						if (MethodCallback != null)
							MethodCallback (a);
					}
				}

				seen [dir] = true;
			}

			return assemblies;
		}

		static public List<Assembly> ScanDirectoryForAssemblies (string dir)
		{
			if (String.IsNullOrEmpty (dir) || !Directory.Exists (dir))
				return null;

			List<Assembly> assemblies = new List<Assembly> ();
			
			DirectoryInfo dir_info = new DirectoryInfo (dir);

			foreach (FileInfo file_info in dir_info.GetFiles ()) {
				if (file_info.Extension == ".dll") {
					Assembly a = Assembly.LoadFrom (file_info.FullName);
					assemblies.Add (a);
				}
			}

			return assemblies;
		}

		static public List<Type> ScanAssemblyForClass (Assembly assembly, Type supertype)
		{
			List<Type> subclasses = new List<Type> ();

			foreach (Type t in assembly.GetTypes ()) {
				if (t.IsSubclassOf (supertype) && ! t.IsAbstract)
					subclasses.Add (t);
			}

			return subclasses;
		}
		
		static public List<Type> ScanAssemblyForInterface (Assembly assembly, Type iface)
		{
			List<Type> implementors = new List<Type> ();

			foreach (Type t in assembly.GetTypes ()) {
				if (t.GetInterface (iface.ToString ()) != null && ! t.IsAbstract)
					implementors.Add (t);
			}

			return implementors;
		}

		static public List<Attribute> ScanTypeForAttribute (Type type, Type attribute_type)
		{
			List<Attribute> attrs = new List<Attribute> ();

			Attribute[] attributes = Attribute.GetCustomAttributes (type);
			foreach (Attribute attr in attributes) {
				Type attr_type = attr.GetType ();

				if (attr_type == attribute_type || attr_type.IsSubclassOf (attribute_type))
					attrs.Add (attr);
			}

			return attrs;
		}
	}
}